#include "config/config.hh"
#include "platform/platform.hh"
#include "renderer/renderer.hh"
#include "utils/journal.hh"
#include <iostream>
#include <stdexcept>
#include <csignal>

namespace
{
    namespace ngt = ngintoy;
    using ngt::config::config;
    using ngt::platform::platform;
    using ngt::renderer::renderer;
    using jn = ngt::utils::journal;

    namespace signal
    {
        volatile std::sig_atomic_t quit_request = 0;

        void handler(int /*sig*/)
        {
            if (++quit_request > 1)
                std::exit(2);
        }
    }

    void install_signal_handlers()
    {
        jn::info(" sys ") << "install signal handlers for SIGINT and SIGTERM";
        std::signal(SIGINT , &signal::handler);
        std::signal(SIGTERM, &signal::handler);
    }

    // self validation
    // - there must be at least 1 platform
    // - there must be at least 1 renderer
    void validate()
    {
        platform::validate();
        renderer::validate();
    }

    auto load_config()
    {
        return config{"ngintoy.toml"};
    }

    auto find_platform(config const& conf)
    {
        auto& rs = platform::registry();
        auto plat = rs.find(conf.platform);
        if (plat == rs.end()) {
            auto err = jn::error();
            err << "platform [" + conf.platform + "] not found."
                << "available platforms:";
            for (auto& r: rs) err << "- [" + r.first + "]";
            throw std::runtime_error{""};
        }
        return plat;
    }

    auto find_renderer(config const& conf)
    {
        auto& rs = renderer::registry();
        auto ren = rs.find(conf.renderer);
        if (ren == rs.end()) {
            auto err = jn::error();
            err << "renderer [" + conf.renderer + "] not found."
                << "available renderers:";
            for (auto& r: rs) err << "- [" + r.first + "]";
            throw std::runtime_error{""};
        }
        return ren;
    }
}

int main() try
{
    install_signal_handlers();
    validate();

    auto conf = load_config();
    auto plat = find_platform(conf)->second(conf);
    auto ren  = find_renderer(conf)->second(conf, *plat);

    jn::info(" sys ") << "mainloop";
    while (plat->poll()) {
        if (signal::quit_request) break;
        ren->render();
    }

    std::cerr << "\n\n";
}
catch (std::runtime_error const& err)
{
    if (err.what()[0]) jn::error() << err.what();
    return 1;
}

